const express = require('express')
const http = require('http')
const { WebSocketServer, WebSocket } = require('ws')
const crypto = require('crypto')
const path = require('path')
const fs = require('fs')
const { TextDecoder, TextEncoder } = require('util')

// 确保存储目录存在
const VIDEO_STORAGE_DIR = path.join(__dirname, 'recordings')
if (!fs.existsSync(VIDEO_STORAGE_DIR)) {
  fs.mkdirSync(VIDEO_STORAGE_DIR, { recursive: true })
}

const app = express()
const httpServer = http.createServer(app)
const wss = new WebSocketServer({ port: 3001 }) // WebSocket端口

// 配置
const PORT = 3000
const textDecoder = new TextDecoder()
const textEncoder = new TextEncoder()

// 聊天室核心数据结构
const rooms = new Map() // 房间集合: key=roomId, value={ id, name, users: Map(userId => {id, name, client}) }
const clients = new Map() // 客户端映射: key=WebSocket, value={ userId, roomId, userName, hasIdentified: boolean }
const recordings = new Map() // 录制状态: key=userId, value={ writeStream, filename }

/* -------------------------- 视频录制工具函数 -------------------------- */
// 开始录制视频 (根据type=4)
function startVideoRecording(userId, userName) {
  // 生成文件名：年月日时分秒+用户名 (例如：20231025153045_张三.webm)
  const date = new Date()
  const timestamp = date
    .toISOString()
    .replace(/T/, '') // 移除T分隔符
    .replace(/\..+/, '') // 移除毫秒部分
    .replace(/-/g, '') // 移除连字符
    .replace(/:/g, '') // 移除冒号

  const filename = `${timestamp}_${userName.replace(/\s+/g, '_')}.webm`
  const filePath = path.join(VIDEO_STORAGE_DIR, filename)

  // 创建写入流
  const writeStream = fs.createWriteStream(filePath)

  // 存储录制信息
  recordings.set(userId, {
    writeStream,
    filename,
    filePath,
    startTime: Date.now()
  })

  console.log(`用户${userName}(${userId})开始录制视频: ${filename}`)
  return filename
}

// 停止录制视频
function stopVideoRecording(userId) {
  const recording = recordings.get(userId)
  if (recording) {
    recording.writeStream.end()
    const duration = Math.round((Date.now() - recording.startTime) / 1000)
    console.log(`用户${userId}停止录制视频: ${recording.filename} (时长: ${duration}秒)`)
    const result = {
      filename: recording.filename,
      filePath: recording.filePath,
      duration
    }
    recordings.delete(userId)
    return result
  }
  return null
}

// 处理视频帧数据（保存到文件）
function handleVideoFrame(userId, frameBuffer) {
  const recording = recordings.get(userId)
  if (recording && recording.writeStream.writable) {
    // 将视频帧写入文件
    recording.writeStream.write(frameBuffer)
  }
}

/* -------------------------- 房间管理工具函数 -------------------------- */
// 创建房间（确保4位数字房间号）
function createRoom(roomId, roomName = '') {
  if (!rooms.has(roomId) && /^\d{4}$/.test(roomId)) {
    rooms.set(roomId, {
      id: roomId,
      name: roomName || `房间${roomId}`,
      users: new Map() // userId => { id, name, client }
    })
    console.log(`创建新房间: ${roomId} (${roomName})`)
  }
  return rooms.get(roomId)
}

// 获取房间列表
function getRoomList() {
  return Array.from(rooms.entries()).map(([id, room]) => ({
    id,
    name: room.name,
    userCount: room.users.size
  }))
}

// 广播消息到房间内所有用户（除发送者）
function broadcastToRoom(roomId, messageBuffer, excludeClient) {
  const room = rooms.get(roomId)
  if (!room) return

  room.users.forEach(user => {
    if (user.client !== excludeClient && user.client.readyState === WebSocket.OPEN) {
      user.client.send(messageBuffer)
    }
  })
}

/* -------------------------- 二进制数据处理 -------------------------- */
// 解包二进制数据
function unpackData(unKnowBuffer) {
  let buffer

  // 处理Node.js Buffer
  if (unKnowBuffer instanceof Buffer) {
    buffer = unKnowBuffer.buffer.slice(unKnowBuffer.byteOffset, unKnowBuffer.byteOffset + unKnowBuffer.byteLength)
  }
  // 处理标准ArrayBuffer
  else if (unKnowBuffer instanceof ArrayBuffer) {
    buffer = unKnowBuffer
  } else {
    throw new Error('Unsupported data type: ' + typeof unKnowBuffer)
  }
  const view = new DataView(buffer)

  // 读取类型
  const type = view.getUint8(0)

  // 读取targetId长度
  const targetIdLength = view.getUint8(1)
  // 读取targetId
  const targetIdBytes = new Uint8Array(buffer, 3, targetIdLength)
  const targetId = new TextDecoder().decode(targetIdBytes)

  // 读取数据
  const dataOffset = 2 + targetIdLength
  const dataLength = buffer.byteLength - dataOffset
  const data = buffer.slice(dataOffset)

  return { type, targetId, data }
}

// 处理二进制媒体数据
function handleBinaryMediaData(client, buffer) {
  const clientInfo = clients.get(client)
  // 验证用户是否已完成身份认证
  if (!clientInfo || !clientInfo.hasIdentified) {
    console.warn('无法处理媒体数据：客户端未完成身份认证')
    return sendError(client, '请先完成身份认证')
  }

  if (!clientInfo.roomId) {
    console.warn('无法处理媒体数据：客户端未加入房间')
    return sendError(client, '请先加入房间')
  }

  try {
    const { type, targetId, data } = unpackData(buffer)

    // 当type===4时处理视频流存储
    if (type === 4) {
      // 如果还没有开始录制，先启动录制
      if (!recordings.has(clientInfo.userId)) {
        startVideoRecording(clientInfo.userId, clientInfo.userName)
      }
      // 处理视频帧数据
      handleVideoFrame(clientInfo.userId, Buffer.from(data))
    }

    const room = rooms.get(clientInfo.roomId)
    if (!room) return sendError(client, '房间不存在')

    // 群聊房间转发给所有用户，私聊转发给指定用户
    if (targetId === clientInfo.roomId) {
      // 房间广播（排除自己）
      room.users.forEach(user => {
        if (user.id !== clientInfo.userId && user.client.readyState === WebSocket.OPEN) {
          user.client.send(buffer)
        }
      })
    } else {
      // 私聊转发
      const targetUser = room.users.get(targetId)
      if (targetUser?.client.readyState === WebSocket.OPEN) {
        targetUser.client.send(buffer)
      } else {
        sendError(client, '目标用户不存在或已离线')
      }
    }
  } catch (error) {
    console.error('处理媒体数据失败:', error)
    sendError(client, '媒体数据处理失败：' + error.message)
  }
}

/* -------------------------- 文本消息处理 -------------------------- */
function handleTextMessage(client, message) {
  try {
    const clientInfo = clients.get(client)
    if (!clientInfo) return sendError(client, '客户端信息不存在')

    // 验证身份：除了identify消息外，其他消息都需要先完成身份认证
    if (message.type !== 'identify' && !clientInfo.hasIdentified) {
      return sendError(client, '请先输入昵称完成身份认证')
    }

    const { type, data } = message

    // 根据消息类型处理
    switch (type) {
      // 客户端身份标识（必须在连接后发送）
      case 'identify':
        if (data.userName) {
          clientInfo.userName = data.userName
          clientInfo.hasIdentified = true // 标记为已完成身份认证
          console.log(`用户标识更新：${clientInfo.userId} -> ${data.userName}`)
          sendSuccess(client, 'identify', { success: true })

          // 身份认证完成后，立即发送房间列表
          sendSuccess(client, 'roomList', getRoomList())
        }
        break

      // 创建房间
      case 'createRoom':
        if (!data.roomId || !/^\d{4}$/.test(data.roomId)) {
          return sendError(client, '房间号必须是4位数字')
        }

        if (rooms.has(data.roomId)) {
          return sendError(client, `房间${data.roomId}已存在`)
        }

        // 创建房间并加入
        const newRoom = createRoom(data.roomId, data.roomName)
        if (newRoom) {
          // 添加用户到房间
          newRoom.users.set(clientInfo.userId, {
            id: clientInfo.userId,
            name: clientInfo.userName,
            client
          })
          clientInfo.roomId = data.roomId

          // 发送创建成功消息
          sendSuccess(client, 'roomCreated', {
            roomId: newRoom.id,
            roomName: newRoom.name,
            users: Array.from(newRoom.users.values()).map(u => ({
              id: u.id,
              name: u.name
            }))
          })

          // 广播房间列表更新
          broadcastRoomListUpdate()
        }
        break

      // 加入房间
      case 'joinRoom':
        if (!data.roomId || !/^\d{4}$/.test(data.roomId)) {
          return sendError(client, '房间号必须是4位数字')
        }

        const room = rooms.get(data.roomId)
        if (!room) {
          return sendError(client, `房间${data.roomId}不存在`)
        }

        // 检查是否已在房间中
        if (clientInfo.roomId === data.roomId) {
          return sendError(client, '您已在该房间中')
        }

        // 先离开当前房间（如果有）
        if (clientInfo.roomId) {
          leaveRoom(client, clientInfo.roomId)
        }

        // 加入新房间
        room.users.set(clientInfo.userId, {
          id: clientInfo.userId,
          name: clientInfo.userName,
          client
        })
        clientInfo.roomId = data.roomId

        // 通知客户端加入成功
        sendSuccess(client, 'roomJoined', {
          roomId: room.id,
          roomName: room.name,
          users: Array.from(room.users.values()).map(u => ({
            id: u.id,
            name: u.name
          }))
        })

        // 广播新用户加入
        broadcastToRoom(
          room.id,
          JSON.stringify({
            type: 'userJoined',
            data: {
              user: {
                id: clientInfo.userId,
                name: clientInfo.userName
              },
              userCount: room.users.size
            }
          }),
          client
        )
        break

      // 离开房间
      case 'leaveRoom':
        if (clientInfo.roomId) {
          // 离开房间时停止录制
          stopVideoRecording(clientInfo.userId)

          leaveRoom(client, clientInfo.roomId)
          sendSuccess(client, 'roomLeft', { roomId: clientInfo.roomId })
          clientInfo.roomId = null
          // 离开房间后，发送最新房间列表
          sendSuccess(client, 'roomList', getRoomList())
        }
        break

      // 停止录制
      case 'stopRecording':
        const result = stopVideoRecording(clientInfo.userId)
        if (result) {
          sendSuccess(client, 'recordingStopped', {
            message: '录制已停止',
            ...result
          })
        } else {
          sendError(client, '没有正在进行的录制')
        }
        break

      // 获取房间列表
      case 'getRoomList':
        sendSuccess(client, 'roomList', getRoomList())
        break

      // 聊天消息
      case 'chatMessage':
        if (!data.roomId) return sendError(client, '房间ID不能为空')

        const chatRoom = rooms.get(data.roomId)
        if (!chatRoom) return sendError(client, '房间不存在')

        const chatMsgBuffer = JSON.stringify({
          type: 'chatMessage',
          data: {
            ...data,
            senderId: clientInfo.userId,
            senderName: clientInfo.userName
          }
        })
        // 广播给房间内所有人（包括自己）
        broadcastToRoom(data.roomId, chatMsgBuffer, null)
        break

      // 音视频状态消息
      case 'videoStatus':
      case 'audioStatus':
      case 'startCall':
      case 'endCall':
        if (data.roomId && data.targetId) {
          const targetRoom = rooms.get(data.roomId)
          const targetUser = targetRoom?.users.get(data.targetId)

          if (targetUser?.client.readyState === WebSocket.OPEN) {
            targetUser.client.send(
              JSON.stringify({
                ...message,
                senderId: clientInfo.userId,
                senderName: clientInfo.userName
              })
            )
          } else {
            sendError(client, '目标用户不存在')
          }
        } else if (type === 'endCall' && data.roomId) {
          // 结束通话时停止录制
          stopVideoRecording(clientInfo.userId)

          // 结束通话广播给房间所有人
          broadcastToRoom(
            data.roomId,
            JSON.stringify({
              ...message,
              senderId: clientInfo.userId,
              senderName: clientInfo.userName
            }),
            client
          )
        }
        break

      default:
        sendError(client, `不支持的消息类型: ${type}`)
    }
  } catch (error) {
    console.error('文本消息处理错误:', error)
    sendError(client, '消息格式错误：' + error.message)
  }
}

/* -------------------------- 通用工具函数 -------------------------- */
// 发送成功消息
function sendSuccess(client, type, data) {
  if (client.readyState !== WebSocket.OPEN) return

  const responseBuffer = JSON.stringify({
    type,
    data
  })
  client.send(responseBuffer)
}

// 发送错误消息
function sendError(client, message) {
  if (client.readyState !== WebSocket.OPEN) return

  const errorBuffer = JSON.stringify({
    type: 'error',
    message
  })
  client.send(errorBuffer)
}

// 离开房间
function leaveRoom(client, roomId) {
  const clientInfo = clients.get(client)
  if (!clientInfo) return

  const room = rooms.get(roomId)
  if (!room) return

  // 移除用户
  room.users.delete(clientInfo.userId)

  // 广播用户离开
  broadcastToRoom(
    roomId,
    JSON.stringify({
      type: 'userLeft',
      data: {
        userId: clientInfo.userId,
        userName: clientInfo.userName,
        userCount: room.users.size
      }
    }),
    client
  )

  // 房间为空时删除房间
  if (room.users.size === 0) {
    rooms.delete(roomId)
    console.log(`房间${roomId}已解散（无用户）`)
    // 广播房间列表更新
    broadcastRoomListUpdate()
  }
}

// 广播房间列表更新
function broadcastRoomListUpdate() {
  const roomListBuffer = JSON.stringify({
    type: 'roomList',
    data: getRoomList()
  })

  // 发送给所有已认证但未在房间中的客户端
  clients.forEach((info, client) => {
    if (info.hasIdentified && !info.roomId && client.readyState === WebSocket.OPEN) {
      client.send(roomListBuffer)
    }
  })
}

/* -------------------------- WebSocket连接处理 -------------------------- */
wss.on('connection', client => {
  // 生成唯一用户ID
  const userId = `user_${crypto.randomUUID().slice(0, 8)}`
  const clientInfo = {
    userId,
    roomId: null, // 初始未加入房间
    userName: `用户${userId.slice(-6)}`, // 默认用户名
    hasIdentified: false // 标记是否已完成身份认证
  }

  clients.set(client, clientInfo)
  console.log(`新连接：${userId}（总连接数：${clients.size}）`)

  // 发送连接成功消息
  client.send(
    JSON.stringify({
      type: 'connectionSuccess',
      data: clientInfo
    })
  )

  // 处理消息（统一以Buffer接收）
  client.on('message', dataBuffer => {
    try {
      // 尝试解析为文本消息
      const data = JSON.parse(dataBuffer)
      handleTextMessage(client, data)
    } catch (error) {
      // 解析失败视为二进制数据
      handleBinaryMediaData(client, dataBuffer)
    }
  })

  // 连接关闭
  client.on('close', () => {
    const info = clients.get(client)
    if (info) {
      console.log(`连接关闭：${info.userId}（剩余连接数：${clients.size - 1}）`)

      // 停止录制并离开房间
      stopVideoRecording(info.userId)
      if (info.roomId) {
        leaveRoom(client, info.roomId)
      }

      clients.delete(client)
    }
  })

  // 错误处理
  client.on('error', error => {
    console.error('WebSocket错误:', error)
  })
})

// 在你的Express服务器中添加
app.get('/recordings', (req, res) => {
  // 读取recordings目录下的所有WebM文件
  fs.readdir(VIDEO_STORAGE_DIR, (err, files) => {
    if (err) {
      return res.status(500).json({ error: '无法读取录制文件' })
    }
    // 筛选出WebM文件
    const webmFiles = files.filter(file => file.endsWith('.webm'))
    res.json({ recordings: webmFiles })
  })
})

// 提供视频文件访问
app.get('/recordings/:filename', (req, res) => {
  const filePath = path.join(VIDEO_STORAGE_DIR, req.params.filename)
  // 检查文件是否存在
  if (fs.existsSync(filePath)) {
    res.sendFile(filePath)
  } else {
    res.status(404).json({ error: '文件不存在' })
  }
})

/* -------------------------- HTTP服务 -------------------------- */
app.use(express.static(path.join(__dirname, 'public')))

// 启动服务器
httpServer.listen(PORT, () => {
  console.log(`HTTP服务器运行在 http://localhost:${PORT}`)
})

wss.on('listening', () => {
  console.log(`WebSocket服务器运行在 ws://localhost:3001`)
})

/* -------------------------- 异常处理 -------------------------- */
process.on('unhandledRejection', error => {
  console.error('未处理的Promise异常:', error)
})

process.on('SIGINT', () => {
  console.log('服务器关闭中...')
  // 关闭所有录制流
  recordings.forEach((recording, userId) => {
    recording.writeStream.end()
    console.log(`强制关闭用户${userId}的录制：${recording.filename}`)
  })
  httpServer.close()
  wss.close()
  process.exit(0)
})
